package org.dexter.model.vo
{
	import flash.utils.Dictionary;
		
	public dynamic class VOBaseArray extends Array
	{
		protected var dataMap:Object;
		protected var mapKey:String;
		protected var subClass:Class;
		protected var typeMaps:Object;
		protected var typeKeys:Object;
		public function VOBaseArray(c:Class = null,mk:String = null,typeKey:Object = null)
		{
			super();
			subClass = c;
			if(mk)setDataMap(mk);
			if(typeKey)setTypeMaps(typeKey);
		}
		public function setArray(array:Array):VOBaseArray{
			if(array && array.length){
				if(length)splice(0,length);
				push.apply(this,array);
			}
			if(subClass)setClass(subClass);
			if(mapKey)setDataMap(mapKey);
			if(typeKeys)setTypeMaps(typeKeys);
			return this;
		}
		public function setMap(object:Object):VOBaseArray{
			var array:Array = [];
			for each(var o:Object in object){
				array.push(o);
			}
			return setArray(array);
		}
		protected function getMapKey(vo:VOBase):*{
			var o:Object = getItemValue(vo,mapKey);
			return typeof(o)=="object"?o:String(o);
		}
		protected function getTypeKey(vo:VOBase):Object{
			var keys:Object = {};
			for(var key:String in typeKeys){
				var o:Object = getItemValue(vo,key);
				keys[key] = (typeof(o)=="object"?o:String(o));
			}
			return keys;
		}
		protected function getItemValue(vo:VOBase,s:String):Object{
			var array:Array = s.split(".");
			var o:* = vo;
			for each(var s:String in array){
				o = o[s];
			}
			return o;
		}
		protected function setItemValue(vo:VOBase,name:String,value:*):void{
			var array:Array = name.split(".");
			var o:* = vo;
			if(array.length>1)
				for(var i:int;i<array.length-1;i++){
					o = o[array[i]];
				}
			o[array[array.length-1]] = value;
		}
		public function setDataMap(name:String):void{
			mapKey = name;
			for each(var vo:VOBase in this){
				setMapData(vo);
			}
		}
		public function setTypeMaps(key:Object):void{
			typeKeys = key;
			typeMaps = {};
			for each(var vo:VOBase in this){
				setTypeData(vo);
			}
		}
		public function setClass(c:Class):void{
			for(var i:int;i<length;i++){
				if(this[i] is c)continue;
				var vo:VOBase = new c() as VOBase;
				vo.convertFromObject(this[i]);
				setItem(vo,i);
			}
		}
		public function setItem(vo:VOBase,index:int = -1):void{
			switch(index){
				case -1:
					push(vo);
					break;
				case -2:
					if(indexOf(vo)==-1)push(vo);
					break;
				default:
					this[index] = vo;
			}
			if(mapKey)setMapData(vo);
			if(typeKeys)setTypeData(vo);
		}
		public function getItemAt(index:int):VOBase{
			return this.length>index?this[index]:null;
		}
		public function deleteItem(vo:VOBase):void{
			if(indexOf(vo)==-1)return;
			splice(indexOf(vo),1);
			if(dataMap)delete dataMap[getMapKey(vo)];
			if(typeMaps){
				for(var type:String in typeMaps){
					typeMaps[type][getItemValue(vo,type)].deleteItem(vo);
				}
			}
		}
		public function toArray():Array{
			return this.concat();
		}
		public function getItemByKey(key:Object):VOBase{
			return dataMap?dataMap[key]:null;
		}
		public function getItemsByType(type:String,value:*):VOBaseArray{
			if(typeof(value)!="object"){
				value = String(value);
			}
			var o:Object = getItemsMapByType(type);
			return o&&o[value]?o[value]:new VOBaseArray();
		}
		public function getItemsMapByType(type:String):Object{
			return typeMaps[type];
		}
		protected function setMapData(vo:VOBase):void{
			var key:Object = getMapKey(vo);
			if(!dataMap){
				if(key is String){
					dataMap = {};
				}else{
					dataMap = new Dictionary(true);
				}
			}
			dataMap[key] = vo;
		}
		protected function setTypeData(vo:VOBase):void{
			var keys:Object = getTypeKey(vo);
			for(var key:String in keys){
				if(!typeMaps[key]){
					if(keys[key] is String){
						typeMaps[key] = {};
					}else{
						typeMaps[key] = new Dictionary(true);
					}
				}
				if(!typeMaps[key][keys[key]]){
					var o:Object;
					if(typeKeys[key]){
						o = {};
						o[typeKeys[key]] = null;
					}
					typeMaps[key][keys[key]] = new VOBaseArray(null,null,o);
				}
				typeMaps[key][keys[key]].setItem(vo);
			}
		}
		public function clear():void{
			splice(0,length);
			if(mapKey)dataMap=null;
			if(typeKeys)typeMaps=null;
		}
		public function getRandomItem():VOBase{
			if(length)
				return this[Math.random()*length>>0];
			return null;
		}
		public function changeItemProperty(item:VOBase,property:String,value:*):void{
			var oldValue:* = getItemValue(item,property);
			if(oldValue==value)return;
			var typeMapProperty:Object;
			if(typeMaps&&typeMaps[property]){
				typeMapProperty = typeMaps[property];
			}
			if(typeMapProperty&&typeMapProperty[oldValue]){
				typeMapProperty[oldValue].deleteItem(item);
			}
			setItemValue(item,property,value);
			if(typeMapProperty){
				if(!typeMapProperty[value]){
					var o:Object;
					if(typeKeys[property]){
						o = {};
						o[typeKeys[property]] = null;
					}
					typeMapProperty[value] = new VOBaseArray(null,null,o);
				}
				typeMapProperty[value].setItem(item);
			}
		}
		AS3 override function filter(callBack:Function,thisObject:*=null):Array{
			var array:VOBaseArray = new VOBaseArray();
			array.setArray(super.filter(callBack,thisObject));
			return array;
		}
	}
}